#include "udpServer.hpp"
#include "onlineUser.hpp"
#include <stdio.h>
#include <fstream>
#include <memory>
#include <unordered_map>

using namespace std;
using namespace Server;

const std::string dictTxt = "./dict.txt";
unordered_map<string, string> dict;

static void Usage(string proc)
{
    cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}

// 切割字符串
static bool cutString(const string &target, string *s1, string *s2, const string &sep)
{
    // hello:你好
    auto pos = target.find(sep);
    if (pos == string::npos)
        return false;
    *s1 = target.substr(0, pos);
    *s2 = target.substr(pos + sep.size());
    return true;
}

// 把dict.txt内容加载到 (unordered_map) dict中
static void initDict()
{
    // 以二进制方式打开dictTxt文件
    ifstream in(dictTxt, std::ios::binary);
    if (!in.is_open())
    {
        cerr << "open file " << dictTxt << " error" << endl;
        exit(OPEN_ERR);
    }

    // 按行读取文件内容，把其切割后，按照key value放入dict中
    string line;
    string key, value;
    while (getline(in, line))
    {
        // cout<<line<<endl;
        if (cutString(line, &key, &value, ":"))
        {
            dict.insert(make_pair(key, value));
        }
    }
    in.close();
    cout << "load dict success" << endl;
}

static void debugDictPrint()
{
    for (auto &dic : dict)
    {
        cout << dic.first << " # " << dic.second << endl;
    }
}

// demo1 一个简易的在线查询字典
void handlerMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    // 就可以对message进行特定的业务处理，而不关心message怎么来的 ---- server通信和业务逻辑解耦！
    string response_message;
    auto iter = dict.find(message);
    if (iter == dict.end())
        response_message = "unknown";
    else
        response_message = iter->second;

    // cout<<response_message<<endl;

    // 开始返回给客户端
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    socklen_t clen = sizeof(client);

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str()); // 把字符型ip转化为整型ip,把主机序列转化为网络序列
    // std::cout << "send to : [" << clientip << ": " << clientport << std::endl;

    sendto(sockfd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&client, clen);
}

// demo2 模拟让客户端进行命令行操作，服务端接收操作并返回相应操作结果
void execCommand(int sockfd, string clientip, uint16_t clientport, string cmd)
{
    // 0.禁止一些非法操作
    if (cmd.find("rm") != string::npos || cmd.find("mv") != string::npos || cmd.find("rmdir") != string::npos)
    {
        cerr << clientip << ":" << clientport << " 正在做一个非法的操作: " << cmd << endl;
        return;
    }
    // 1.cmd解析
    // 2.返回cmd内容
    string response;
    FILE *fp = popen(cmd.c_str(), "r"); // popen功能：创建一个子进程，将收到的命令进行程序替换执行命令，并把命令结果放到文件里面，以参数选择的打开方式打开该文件，并返回该文件的文件指针
    if (fp == nullptr)
        response = cmd + "exec failed";
    char line[1024];
    while (fgets(line, sizeof(line), fp)) // fgets按照字节数获取文件内容到指定位置，直到获取失败，获取失败返回NULL
    {
        response += line;
    }
    pclose(fp); // popen对应关闭文件pclose

    // 开始返回给客户端
    struct sockaddr_in client;
    bzero(&client, sizeof(client));
    socklen_t clen = sizeof(client);

    client.sin_family = AF_INET;
    client.sin_port = htons(clientport);
    client.sin_addr.s_addr = inet_addr(clientip.c_str()); // 把字符型ip转化为整型ip,把主机序列转化为网络序列
    // std::cout << "send to : [" << clientip << ": " << clientport << std::endl;

    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, clen);
}

// demo3 实现一个简易版在线群聊
OnlineUser onlineuser;
void routeMessage(int sockfd, string clientip, uint16_t clientport, string message)
{
    //1.用户登录管理模块
    //2.接收信息、群发消息模块
    if(message=="online") onlineuser.addUser(clientip,clientport);
    if(message=="offline") onlineuser.delUser(clientip,clientport);
    if(onlineuser.isOnline(clientip,clientport))
    {
        //消息的路由
        onlineuser.broadcastMessage(sockfd,clientip,clientport,message);
    }
    else
    {
        struct sockaddr_in client;
        bzero(&client, sizeof(client));

        client.sin_family = AF_INET;
        client.sin_port = htons(clientport);
        client.sin_addr.s_addr = inet_addr(clientip.c_str());

        string response = "你还没有上线，请先上线，运行: online";
        sendto(sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&client,sizeof(client));
    }

}

// ./udpServer port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    uint16_t port = atoi(argv[1]);
    // string ip = argv[1];

    initDict();
    debugDictPrint();

    //std::unique_ptr<udpServer> usvr(new udpServer(handlerMessage, port));
    //std::unique_ptr<udpServer> usvr(new udpServer(execCommand, port));
    std::unique_ptr<udpServer> usvr(new udpServer(routeMessage, port));

    usvr->initServer();
    usvr->start();

    return 0;
}
